
#include <BFile>

#include <BObcDocument>
#include <BOscDocument>
#include "member_BOscDocument.h"

using namespace BWE;

#define member					(*(member_BOscDocument*)_ptr)
#define member_allocate()		_ptr = new member_BOscDocument(this)
#define member_release()		delete (member_BOscDocument*)_ptr

BOscDocument::BOscDocument()
{
	member_allocate();
}
BOscDocument::~BOscDocument()
{
	member_release();
}

bool BOscDocument::setFormat(const BString& format)
{
	return member.sondoc.setFormat(format);
}
const BString& BOscDocument::format() const
{
	return member.sondoc.format();
}

BObject* BOscDocument::load(const BString& fileName)
{
	BFile file(fileName);
	if (!file.open(IO_Read))
	{
		message() << "OSC Error : Read file failed : Can't open file " << fileName;
		return 0;
	}
	return read(file);
}
BObject* BOscDocument::read(const BStream& stream)
{
	member.readObjectMap.clear();
	member.writeObjectMap.clear();

	stream.seek(0);
	BSonNodeHolder sonode = member.sondoc.read(stream);
	if (sonode == 0)
		return 0;

	BObject* object = readObject(sonode);
	return object;
}

bool BOscDocument::load(const BString& fileName, BObject* object)
{
	BFile file(fileName);
	if (!file.open(IO_ReadText))
	{
		message() << "OSC Error : Read file failed : Can't open file " << fileName;
		return false;
	}
	return read(file, object);
}
bool BOscDocument::save(const BString& fileName, const BObject* object) const
{
	BFile file(fileName);
	if (!file.open(IO_WriteText))
	{
		member.message() << "OSC Error : Write file failed : Can't open file " << fileName;
		return false;
	}
	return write(file, object);
}

bool BOscDocument::read(const BStream& stream, BObject* object)
{
	stream.seek(0);
	BSonNode* sonode = member.sondoc.read(stream);
	if (sonode == 0)
		return false;
	return read(sonode, object);
}
bool BOscDocument::write(BStream& stream, const BObject* object) const
{
	if (!object)
		return false;

	member.readObjectMap.clear();
	member.writeObjectMap.clear();

	BHolder<BSonNode> sonode = writeObject(object);
	if (!sonode)
	{
		member.message() << "OSC Error : Write node failed : " << object->className();
		return false;
	}
	return member.sondoc.write(stream, sonode);
}

bool BOscDocument::read(const BSonNode* sonode, BObject* object)
{
	member.readObjectMap.clear();
	member.writeObjectMap.clear();
	for (int i = 0; i < object->typeCount(); i++)
	{
		const char* type = object->typeName(i);
		member_BOscDocument::readFunc read = member.readFuncMap(type);
		if (!read)
			continue;
		if (false == (member.*read)(sonode, object))
			message() << "OSC Error : Read object failed " << type;
	}
	return true;
}
BSonNode* BOscDocument::write(const BObject* object) const
{
	if (!object)
		return 0;

	member.readObjectMap.clear();
	member.writeObjectMap.clear();

	BSonNode* sonode = writeObject(object);
	if (!sonode)
	{
		member.message() << "OSC Error : Write node failed : " << object->className();
	}
	return sonode;
}

BString& BOscDocument::message()
{
	return member.message();
}
const BString& BOscDocument::message() const
{
	return member.message();
}

BObject* BOscDocument::readObject(const BSonNode* node)
{
	const BString& idStr = node->attrib("id");
	int id = idStr.toInt();
	if (id > 0)
	{
		auto it = member.readObjectMap.find(id);
		if (it.valid())
		{
			return it.value();
		}
	}
	const BString& name = node->name();
	BObject* object = 0;
	if (name == BScene::TypeName())
		object = new BScene();
	else if (name == BNode::TypeName())
		object = new BNode();
	else if (name == BLight::TypeName())
		object = new BLight();
	else if (name == BCamera::TypeName())
		object = new BCamera();
	else if (name == BGroup::TypeName())
		object = new BGroup();
	else if (name == BMesh::TypeName())
		object = new BMesh();
	else if (name == BGeometry::TypeName())
		object = new BGeometry();
	else if (name == BCone::TypeName())
		object = new BCone();
	else if (name == BCube::TypeName())
		object = new BCube();
	else if (name == BCylinder::TypeName())
		object = new BCylinder();
	else if (name == BSphere::TypeName())
		object = new BSphere();
	else if (name == BGeoSphere::TypeName())
		object = new BGeoSphere();
	else if (name == BTorus::TypeName())
		object = new BTorus();
	else if (name == BTube::TypeName())
		object = new BTube();
	else if (name == BSurface::TypeName())
		object = new BSurface();
	else if (name == BTerrain::TypeName())
		object = new BTerrain();
	else if (name == BBillboard::TypeName())
		object = new BBillboard();
	else if (name == BParticle::TypeName())
		object = new BParticle();
	else if (name == BStyle::TypeName())
		object = new BStyle();
	else if (name == BImage::TypeName())
		object = new BImage();
	else if (name == BPixmap::TypeName())
		object = new BPixmap();
	else if (name == BMaterial::TypeName())
		object = new BMaterial();
	else if (name == BScript::TypeName())
		object = new BScript();
	else if (name == BSource::TypeName())
		object = new BSource();
	else
	{
		message() << "OSC Error : Create object failed :  " << name;
		return 0;
	}
	for (int i = 0; i < object->typeCount(); i++)
	{
		const char* type = object->typeName(i);
		member_BOscDocument::readFunc read = member.readFuncMap(type);
		if (!read)
			continue;
		if (false == (member.*read)(node, object))
			message() << "OSC Error : Read object failed " << name;
	}
	if (id > 0)
	{
		member.readObjectMap[id] = object;
	}
	return object;
}
BSonNode* BOscDocument::writeObject(const BObject* object) const
{
	const char* type = object->className();
	int id = member.writeObjectMap(object);
	if (id > 0)
	{
		BSonNode* node = new BSonNode(type);
		node->setAttrib("id", id);
		return node;
	}
	BSonNode* node = new BSonNode(type);
	for (int i = 0; i < object->typeCount(); i++)
	{
		BString type = object->typeName(i);
		member_BOscDocument::writeFunc write = member.writeFuncMap(type);
		if (!write)
			continue;
		if (!(member.*write)(node, object))
			member.message() << "OSC Error : Write object failed " << type;
	}
	if (object->ref_count() > 1)
	{
		id = member.writeObjectMap.size() + 1;
		member.writeObjectMap.insert(object, id);
		node->setAttrib("id", id);
	}
	return node;
}
